﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net;

namespace OnkyoController
{
    public enum OnkyoControllerReturnCodes
    {
        ConnectSuccess,
        ConnectFail,
        DisconnectSuccess,
        DisconnectFail
    }

    public sealed class OnkyoIoHandler : IDisposable
    {
        private TcpClient _client;
        private NetworkStream _ns;
        private StreamWriter _writer;
        private StreamReader _reader;

        private readonly List<IOnkyoCommandListener> _commandListeners;

        public OnkyoIoHandler()
        {
            _commandListeners = new List<IOnkyoCommandListener>();
        }

        public void AddCommandListener(IOnkyoCommandListener listener)
        {
            _commandListeners.Add(listener);
        }

        public OnkyoControllerReturnCodes Connect(IPAddress ipAddress, int port)
        {
            _client = new TcpClient();
            try
            {
                _client.Connect(ipAddress, port);
                _ns = _client.GetStream();
                _writer = new StreamWriter(_ns);
                _reader = new StreamReader(_ns);

                var bgReader = new BackgroundWorker();
                bgReader.DoWork += (o, args) => ParseMessage();
                bgReader.RunWorkerAsync();
            }
            catch (SocketException)
            {
                return OnkyoControllerReturnCodes.ConnectFail;
            }

            return OnkyoControllerReturnCodes.ConnectSuccess;
        }

        public OnkyoControllerReturnCodes Disconnect()
        {
            if (_client != null)
            {
                try
                {
                    _client.Close();
                }
                catch (Exception)
                {
                    return OnkyoControllerReturnCodes.DisconnectFail;
                }
            }

            return OnkyoControllerReturnCodes.DisconnectSuccess;
        }

        public void ChangeVolume(double volume)
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.VOLUME_SET, volume);
        }

        public void GetVolumeLevel()
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.VOLUME_LEVEL);
            Thread.Sleep(200);
        }

        public void GetArtist()
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.ARTIST);
            Thread.Sleep(200);
        }

        public void GetSongTitle()
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.SONG_TITLE);
            Thread.Sleep(200);
        }

        public void ChangeInput(String input)
        {
            WriteCommand(input);
        }

        public void NextTrack()
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.NEXT_TRACK);
        }

        public void PreviousTrack()
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.PREVIOUS_TRACK);
        }

        public void Play()
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.PLAY);
        }

        public void Pause()
        {
            WriteCommand(OnkyoController.Resources.OnkyoControllerCommands.PAUSE);
        }

        public void ParseMessage()
        {
            try
            {
                string line;
                while ((line = _reader.ReadLine()) != null)
                {
                    if (line.Length == 0)
                    {
                        continue;
                    }

                    if (line.Contains("NTM"))
                    {
                        foreach (var onkyoCommandListener in _commandListeners)
                        {
                            onkyoCommandListener.OnPlayTimeChanged(line.Substring(line.IndexOf("NTM", System.StringComparison.Ordinal) + 3));
                        }
                    }
                    else if (line.Contains("NAT"))
                    {
                        foreach (var onkyoCommandListener in _commandListeners)
                        {
                            onkyoCommandListener.OnArtistChanged(line.Substring(line.IndexOf("NAT", System.StringComparison.Ordinal) + 3));
                        }
                    }
                    else if (line.Contains("NTI"))
                    {
                        foreach (var onkyoCommandListener in _commandListeners)
                        {
                            onkyoCommandListener.OnSongTitleChanged(line.Substring(line.IndexOf("NTI", System.StringComparison.Ordinal) + 3));
                        }
                    }
                    else if (line.Contains("MVL"))
                    {
                        int startIndex = line.IndexOf("MVL", System.StringComparison.Ordinal) + 3;

                        double volumeLevel;
                        
                        if (!double.TryParse(
                            line.Substring(startIndex, line.Length - startIndex - 1),
                            out volumeLevel))
                        {
                            continue;
                        }

                        foreach (var onkyoCommandListener in _commandListeners)
                        {
                            onkyoCommandListener.OnVolumeLevelChanged(volumeLevel);
                        }
                    }
                }
            }
            catch (IOException)
            {
                if (_client == null || !_client.Connected)
                {
                    return;
                }

                ParseMessage();
            }
        }

        public void WriteCommand(String command, double value = double.NaN)
        {
            if (!double.IsNaN(value))
            {
                command += (int)value;
            }

            var code = (char)command.Length;
            var cmd = "ISCP\x00\x00\x00\x10\x00\x00\x00" + code +
                "\x01\x00\x00\x00!1" + command + "\x0D";

            _writer.WriteLine(cmd);
            _writer.Flush();
        }

        public void Dispose()
        {
            if (_client != null)
            {
                _client.Close();
            }
            if (_writer != null)
            {
                _writer.Close();
                _writer.Dispose();
            }
        }
    }
}
